06

您所在的位置:网站首页 spring boot操作日志记录 06

06

2023-06-19 10:11| 来源: 网络整理| 查看: 265

目录

1. 通知类型

2. 通知顺序 

3. 切入点表达式

execution()

annotation() 

 4. 连接点(JoinPoint) 

5. 案例:将CRUD接口的相关操作记录到数据库中

AOP: Aspect Oriented Programming (面向切面编程、面向方面编程),其实就是面向特定方法编程 

优势:代码无侵入、减少重复代码、提高开发效率、维护方便

环境准备

以下代码是从tlias案例中复制过来的

场景:

        1. 案例部分功能运行较慢,定位执行耗时较长的业务方法,此时需要统计每一个业务方 法的执行耗时                  导入依赖:

org.springframework.boot spring-boot-starter-aop

                定义Aop函数:

package pearl.aop; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Slf4j @Component @Aspect public class TimeAspect { @Around("execution(* pearl.service.*.*(..))") public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable { // 1. 记录开始时间 long begin =System.currentTimeMillis(); // 2. 调用原始方法运行 Object result = joinPoint.proceed(); // 3. 记录结束时间,计算方法执行耗时 long end = System.currentTimeMillis(); log.info(joinPoint.getSignature()+"方法执行耗时:{}ms",end-begin); return result; } }

 完成!

核心概念:

连接点: JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)

通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)

切入点: PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用

切面:Aspect,描述通知与切入点的对应关系(通知+切入点)目标对象: Target,通知所应用的对象

1. 通知类型 @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行 需要自己调用ProceedingJoinPoint.proceed()来让原始方法执行,其他通知不需要考虑目标方法执行方法的返回值必须指定为object,来接收原始方法的返回值。@Before:前置通知,此注解标注的通知方法在目标方法前被执行@After:后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行@AfterReturning : 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行@AfterThrowing :异常后通知,此注解标注的通知方法发生异常后执行

补充:@Pointcut 

        该注解的作用是将公共的切点表达式抽取出来,需要用到时引用该切点表达式即可。

2. 通知顺序 

3. 切入点表达式

概念:描述切入点方法的一种表达式 作用:主要用来决定项目中的哪些方法需要加入通知 常见形式:         1.execution(..…)︰根据方法的签名来匹配                                                                                        2.@annotation(...) ︰根据注解匹配

execution()

annotation() 

        1. 自定义注解:

package pearl.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME)//指定运行时生效 @Target(ElementType.METHOD)//指定作用到方法上 public @interface MyLog { }

        2. 在方法前加上自定义的注解

         3. 根据注解匹配切入点表达式 

 4. 连接点(JoinPoint) 

5. 案例:将CRUD接口的相关操作记录到数据库中

日志信息包含:操作人、操作时间、执行方法的全类名、执行方法名、方法运行时参数、返回值、方法执行时长。

步骤:

引入AOP依赖(该文上方有,自行查看)准备数据库表,并引入实体类自定义注解@Log定义切面类,完成记录操作日志的逻辑

数据库表:

-- 操作日志表 create table operate_log( id int unsigned primary key auto_increment comment 'ID', operate_user int unsigned comment '操作人ID', operate_time datetime comment '操作时间', class_name varchar(100) comment '操作的类名', method_name varchar(100) comment '操作的方法名', method_params varchar(1000) comment '方法参数', return_value varchar(2000) comment '返回值', cost_time bigint comment '方法执行耗时, 单位:ms' ) comment '操作日志表';

实体类OperateLog:

@Data @NoArgsConstructor @AllArgsConstructor public class OperateLog { private Integer id; //ID private Integer operateUser; //操作人ID private LocalDateTime operateTime; //操作时间 private String className; //操作类名 private String methodName; //操作方法名 private String methodParams; //操作方法参数 private String returnValue; //操作方法返回值 private Long costTime; //操作耗时 }

OperateLogMapper接口:

@Mapper public interface OperateLogMapper { //插入日志数据 @Insert("insert into operate_log (operate_user, operate_time, class_name, method_name, method_params, return_value, cost_time) " + "values (#{operateUser}, #{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{costTime});") public void insert(OperateLog log); }

定义注解类:

首先创建一个包anno

然后在包里新建Annotation(注解类)Log

@Retention(RetentionPolicy.RUNTIME) //指定运行时生效 @Target(ElementType.METHOD) //指定该注解作用到方法上 public @interface Log { }

定义切面类 LogAspect

package pearl.aop; import com.alibaba.fastjson.JSONObject; import io.jsonwebtoken.Claims; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import pearl.mapper.OperateLogMapper; import pearl.pojo.OperateLog; import pearl.utils.JwtUtils; import javax.servlet.http.HttpServletRequest; import java.time.LocalDateTime; import java.util.Arrays; @Slf4j @Component //标注一个类为Spring容器的Bean,(把普通pojo实例化到spring容器中,相当于配置文件中的) @Aspect //切面类 public class LogAspect { @Autowired private OperateLogMapper operateLogMapper; //要调用该类中的insert方法 @Autowired private HttpServletRequest request; @Around("@annotation(pearl.anno.Log)")//@annotation表示匹配注解类,括号中是要匹配注解的全类名,表示使用该注解的方法都会匹配 public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable { // 获取记录到日志中的信息 // 操作人ID // 获取请求头中的jwt令牌,解析令牌 String jwt = request.getHeader("token"); Claims claims = JwtUtils.parseJWT(jwt); Integer operateUserId = (Integer) claims.get("id"); // 操作时间 LocalDateTime operateTime = LocalDateTime.now(); // 操作类名 String className = joinPoint.getTarget().getClass().getName(); // 操作方法名 String methodName = joinPoint.getSignature().getName(); // 操作方法参数 Object[] args = joinPoint.getArgs(); String methodParams = Arrays.toString(args); long begin = System.currentTimeMillis(); //调用原始方法运行 Object result = joinPoint.proceed(); long end = System.currentTimeMillis(); // 操作方法返回值 String returnValue = JSONObject.toJSONString(result); // 操作耗时 Long costTime = end - begin; //记录日志操作 OperateLog operateLog = new OperateLog(null,operateUserId,operateTime,className,methodName,methodParams,returnValue,costTime); operateLogMapper.insert(operateLog); return result; } }

 在需要的controller接口方法前设置@Log注解即可。(切记不要在登录方法前加)

        



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3